
// We need this define to get the Waitable timer stuff
#include <limits.h>
#include "timer.h"
#include <winbase.h>
#pragma warning(disable:4201)	//mmsystem.h has nameless structs
#include <Mmsystem.h>

#include "lib.h"

Timer::Timer ()
{
	m_htimer = CreateWaitableTimer (NULL, FALSE, NULL);

	m_time = 0;
	m_tid = 0;
	m_id = 0;
	m_done = false;
	InitializeCriticalSection(&m_mutex);
}

Timer::~Timer ()
{
	StopTimer();
	CloseHandle (m_htimer);
	DeleteCriticalSection(&m_mutex);
}

void
Timer::unlock()
{
	LeaveCriticalSection(&m_mutex);
}

void
Timer::lock()
{
	EnterCriticalSection(&m_mutex);
}

HANDLE Timer::GetHandle (void)
{
	return (m_htimer);
}

long Timer::AddDelta (long time, void* data)
{
	return (Add (GetTime() + time, data));
}

long Timer::Add (time_t time, void* data)
{
	TIMEENTRY te;
	ItLE index;
	long id;

	if (time < GetTime())
		return (-1);
	te.id = id = GetId ();
	te.time = time;
	te.data = data;
	index = FindClosest (time);
	AddToList (index, te);

	//printf("TL: %d\n", m_list.size());

	return (id);
}

void Timer::ClearAll (void)
{
	StopTimer();
	m_list.clear ();
}

bool
Timer::Remove (void* who)
{
	//DEBUGMSG(("Timer::Remove: %d", who));

	ItLE index = m_list.begin();
	ItLE ret = m_list.end();

	while (index != m_list.end())
	{
		if (index->data == who)
		{
			ret = index;
			break;
		}
		++index;
	}

	if (ret == m_list.end())
		return false;

	if (ret == m_list.begin())
	{
		StopTimer();
		m_list.pop_front ();
		//TimerCompletion ();
	}
	else
	{
		m_list.erase (ret);
	}

	return true;
}

long Timer::TimerExpired (void*& data)
{
	ItLE index;
	long id;

	index = m_list.begin();
	if (m_done || index == m_list.end())
		return (-1);
	if (m_time == 0)
		m_time = index->time;

	if (m_time != index->time)
		return -1;

	id = index->id;
	data = index->data;

	m_list.pop_front ();

	if ((m_list.size() == 0) || (m_list.begin()->time != 0)  )
		m_done = true;

	return (id);
}

void Timer::TimerCompletion (void)
{
	BOOL val;
	time_t time;
	ItLE index;

	m_done = false;
	m_time = 0;
	time = GetTime();
	index = m_list.begin();
	if (index != m_list.end())
		val = SetTime (index->time-time);
}

//--------------- Protected Members --------------------


long Timer::AddToList (ItLE start, TIMEENTRY te)
{
	BOOL val;
	time_t now;
	ItLE  begin;

	now = GetTime();
	if (start == m_list.begin())
	{
		m_time = 0;
		StopTimer();
		val = SetTime (te.time-now);
	}
	m_list.insert(start, te);
	return (0);
}

ItLE Timer::FindId (long id)
{
	ItLE cur, ret;

	ret = m_list.end();
	cur = m_list.begin();
	while (cur != m_list.end())
	{
		if (cur->id == id)
		{
			ret = cur;
			break;
		}
		++cur;
	}
	return (ret);
}

ItLE Timer::FindClosest (time_t time)
{
	ItLE cur;

	cur = m_list.begin();
	if (m_list.empty())
		return (cur);
	while (cur != m_list.end())
	{
		if (time < cur->time)
			break;
		++cur;
	}
	return (cur);
}

inline time_t Timer::GetTime (void)
{
	return (GetTickCount());
}

BOOL Timer::SetTime (time_t time)
{
	if (time < 0)
		time = 0;
	
	LARGE_INTEGER DueTime;
	DWORD Period;
	
	Period = time;
	DueTime.QuadPart = -(LONGLONG)Period * 10000;
	return (SetWaitableTimer(m_htimer, &DueTime, 0, NULL, NULL, FALSE));
}
//----------------- Private Members -------------------

long Timer::GetId (void)
{
	long val = m_id;

	if (m_id == LONG_MAX)
		m_id = 0;
	else
		++m_id;
	return (val);
}

void
Timer::StopTimer()
{
	CancelWaitableTimer(m_htimer);
}